"""
jackson.chord.core

Provides functionality abstracted from both the APIs and the database backend.
"""
### Start of imports

# This line just better work...
from jackson import debug

# There's debug stuff for the rest
debug.pre_import(__name__)

debug.importing(__name__, 'hashlib', 3)
import hashlib

debug.importing(__name__, 'zfill from string', 3)
from string import zfill

debug.importing(__name__, 'settings from django.conf', 2)
from django.conf import settings

debug.importing(__name__, 'db from jackson.chord')
from jackson.chord import db

debug.importing(__name__, 'jackson.cdn.chord_api')
import jackson.cdn.chord_api

debug.importing(__name__, 'net, Server from jackson.chord')
from jackson.chord import net, Server

debug.importing(__name__, '* from jackson.chord.utils')
from jackson.chord.utils import * # chord utils

debug.importing(__name__, 'key_in_range from jackson.utils')
from jackson.utils import key_in_range

# Import the following to expose them to the other components of Chord.
debug.importing(__name__, 'Lots of stuff from db')
from db import clear_db, get_me, \
    get_predecessor, get_successor, i_am, i_am_active, i_am_leaving, set_finger, \
    update_predecessor, update_status, update_successor, closest_preceding_finger

debug.post_import(__name__)
 
### End of imports

def find_predecessor(key):
    """ Returns a chord.Server object representing the predecessor of key.
    @author: ep
    @param key: a key
    @rtype: a chord.Server object
    @note: this is the iterative implementation given in the Chord paper
    """
    context = [__name__, 'find_predecessor()', []]
    debug.enter(context, { 'key' : key }, 3)
    # Hit database
    me = get_me()
    
    # Start search...
    # If I am predecessor, return me.
    if i_am_predecessor(key):
        debug.leave(context, { 'predecessor' : me }, 3)
        return me
        
    # Otherwise, iteratively search for the predecessor
    server = db.closest_preceding_finger(key)
    # As long as server isn't the predecessor,
    while not net.is_predecessor(server, key):
        # ask server to get us closer to the predecessor.
        server = net.closest_preceding_finger(server, key)
    
    # When that loop terminates, server is the predecessor.
    debug.leave(context, { 'predecessor' : me }, 3)
    return server

def find_successor(key):
    """ Returns a chord.Server object representing the successor of the given key.
    @author: ep
    @param key: a key
    @rtype: a chord.Server object
    @note: should never issue an http request to localhost
    """
    context = [__name__, 'find_successor()', [], 0]
    debug.enter(context, { 'key' : key }, 3)
    # Hit the database.
    me = get_me()
    
    # Find the predecessor of key
    pred = find_predecessor(key)
    
    # Ask for pred's successor
    if i_am(pred):
        succ = db.get_successor()
    else:
        succ = net.get_successor(pred)
    
    # Return key's successor
    debug.leave(context, { 'succ_of_key_in_question' : succ }, 3)
    return succ

def i_am_predecessor(key):
    """ Returns True if key falls between this server and this server's successor in the keyspace.
    @author: ep
    @param key: a key
    """
    context = [__name__, 'i_am_predecessor']
    debug.enter(context, { 'key' : key }, 3)
    # Lookup some info
    my_key = get_me().key
    succ_key = get_successor().key
    
    # Check if I'm the predecessor
    if key_in_range(key, my_key, succ_key):
        flag = True
    else:
        flag = False
        
    debug.leave(context, { 'result' : flag }, 3)
    return flag

def i_am_successor(key):
    """ Returns True if key falls between this server and this server's predecessor in the keyspace.
    @author: ep
    @param key: a key
    """
    context = [__name__, 'i_am_successor()']
    debug.enter(context, { 'key' : key }, 3)
    # Lookup some info
    me = get_me()
    my_key = me.key
    debug.comment(context, "just to be sure: i am " + str(me.ip)+":"+ str(me.port))
    pred = get_predecessor()
    pred_key = pred.key
    debug.comment(context, "and: my predecessor is " + str(pred.ip)+":"+ str(pred.port))
    succ = get_successor()
    debug.comment(context, "successor is " + str(succ.ip)+":"+ str(succ.port))
    debug.comment(context, "is key "+str(key) + " between my pred and i? i hope so")
    # Check if I'm the successor
    if key_in_range(key, pred_key, my_key):
        flag = True
    else:
        flag = False
    
    debug.leave(context, { 'result' : flag }, 3)
    return flag

def initialize_me():
    """ Collects information about this server, constructs a chord.Server object, and adds it to the database.
    @note: this function call updates the database!
    @author: ep
    @rtype: None
    """
    context = [__name__, 'initialize_me()']
    debug.enter(context, None, 3)
    # Gather information about this server
    me = Server()
    me.ip = settings.MY_IP
    me.port = settings.MY_PORT
    me.key = generate_server_key(me.ip, me.port)
    me.status = 'U'
    
    # Enter my information in the finger table
    db.set_finger(0, me)
    debug.leave(context, None, 3)

def trigger_add_keys_event(low, high, server):
    """ Signals software using chord that it is now responsible for keys in the interval (low, high]
    @author: ep
    @param low: key at start of range
    @param high: key at end of range
    @param server: a chord.Server object for the server who used to be responsible for these keys.
    @todo: improve generality - this means integration with other deprecated event-related functions
    @see: chord.client_api
    """
    context = [__name__, 'trigger_add_keys_event()']
    debug.enter(context, { 'low' : low, 'high' : high, 'old_responsible_server' : server }, 3)
    jackson.cdn.chord_api.add_key_range_handler(low, high, server)
    debug.leave(context, None, 3)
    
def trigger_del_keys_event(low, high, server):
    """ Signals software using chord that it is no longer responsible for keys in the interval (low, high]
    @author: ep
    @param low: key at start of range
    @param high: key at end of range
    @param server: a chord.Server object for the server who is now responsible for these keys.
    @todo: improve generality - this means integration with other deprecated event-related functions
    @see: chord.client_api
    """
    context = [__name__, 'trigger_del_keys_event()']
    debug.enter(context, { 'low' : low, 'high' : high, 'newly_responsible_server' : server }, 3)
    jackson.cdn.chord_api.del_key_range_handler(low, high, server)
    debug.leave(context, None, 3)
